/**
 * Copyright 2014 opencxa.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cxa.rpc.session.tests;

import static org.junit.Assert.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import org.cxa.exec.updater.Updater;
import org.cxa.rpc.messages.NotificationRpcMessage;
import org.cxa.rpc.session.SessionManager;
import org.cxa.rpc.session.SessionManager.SessionListenerAdapter;
import org.cxa.rpc.transportChannel.TransportChannel.DisconnectReason;
import org.cxa.rpc.transportChannel.json.webSocket.ClientWebSocketJsonTransportChannel;
import org.cxa.timeUtils.TimeDiff;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


/**
 * @author Christopher Armenio
 *
 */
public class WebSocketJsonSessionTests
{
	private static final String ECHO_SERVER = "ws://echo.websocket.org";
	private static final int TIMEOUT_CONNECT_S = 10;
	private static final int TIMEOUT_DISCONNECT_S = 10;
	
	
	private SessionManager sm = null;
	
	
	@Before
	public void setup() throws IOException
	{	
		// setup our session manager
		if( this.sm == null ) this.sm = new SessionManager(new ClientWebSocketJsonTransportChannel());
	}
	
	
	@After
	public void tearDown()
	{
		// make sure we're closed and all session listeners are unregistered
		this.sm.removeAllSessionListeners();
		this.sm.getTransportChannel().disconnect();
	}
	
	
	@Test
	public void test_keepAlive_idle() throws InterruptedException
	{
		// register for our callbacks
		final Semaphore onConnect = new Semaphore(0);
		final Semaphore onDisconnect = new Semaphore(0);
		
		this.sm.addSessionListener(new SessionListenerAdapter()
		{
			@Override
			public void onSessionOpen() { onConnect.release(); }
			
			@Override
			public void onSessionClose(DisconnectReason reasonIn) { onDisconnect.release(); }
		});
		
		// setup and start our updater
		Updater updater = new Updater(this.sm);
		assertTrue("failed to start updater", updater.start());
		
		// first...connect to the echo server
		((ClientWebSocketJsonTransportChannel)this.sm.getTransportChannel()).connect(ECHO_SERVER);
		
		// wait for a connection
		assertTrue("failed to connect to echo server", onConnect.tryAcquire(TIMEOUT_CONNECT_S, TimeUnit.SECONDS));
		
		// make sure we state connected for longer than timeout period
		TimeDiff td_sessionTimeout = new TimeDiff();
		while( !td_sessionTimeout.isElapsed(SessionManager.SESSION_TIMEOUT_PERIOD_MS*2, TimeUnit.MILLISECONDS) )
		{
			assertFalse("early disconnect detected", onDisconnect.tryAcquire());
		}
		
		// if we made it here, close our connection
		this.sm.getTransportChannel().disconnect();
		
		// wait for closure
		assertTrue("failed to close transport channel", onDisconnect.tryAcquire(TIMEOUT_DISCONNECT_S, TimeUnit.SECONDS));
		
		// stop our updater
		updater.stop();
	}
	
	
	@SuppressWarnings("serial")
	@Test
	public void test_sendNotification() throws InterruptedException
	{
		// register for our callbacks
		final Semaphore onConnect = new Semaphore(0);
		final Semaphore onDisconnect = new Semaphore(0);
		final Semaphore onNotificationReceived = new Semaphore(0);
		
		this.sm.addSessionListener(new SessionListenerAdapter()
		{
			@Override
			public void onSessionOpen() { onConnect.release(); }
			
			@Override
			public void onSessionClose(DisconnectReason reasonIn) { onDisconnect.release(); }
			
			@Override
			public void onNotificationReceived(NotificationRpcMessage msgIn) { onNotificationReceived.release(); }
		});
		
		// setup and start our updater
		Updater updater = new Updater(this.sm);
		assertTrue("failed to start updater", updater.start());
		
		// first...connect to the echo server
		((ClientWebSocketJsonTransportChannel)this.sm.getTransportChannel()).connect(ECHO_SERVER);
		
		// wait for a connection
		assertTrue("failed to connect to echo server", onConnect.tryAcquire(TIMEOUT_CONNECT_S, TimeUnit.SECONDS));
		
		// send the notification
		this.sm.sendMessage(new NotificationRpcMessage("subject", new HashMap<String, Object>()
		{{
		    put("foo", "bar");
		    put("woot", 456);
		}}));
		assertTrue("failed to receive notification", onNotificationReceived.tryAcquire(5, TimeUnit.SECONDS));
		
		// if we made it here, close our connection
		this.sm.getTransportChannel().disconnect();
		
		// wait for closure
		assertTrue("failed to close transport channel", onDisconnect.tryAcquire(TIMEOUT_DISCONNECT_S, TimeUnit.SECONDS));
		
		// stop our updater
		updater.stop();
	}
}

